#region License
/* **********************************************************************************
 * Copyright (c) Roman Ivantsov
 * This source code is subject to terms and conditions of the MIT License
 * for Irony. A copy of the license can be found in the License.txt file
 * at the root of this distribution. 
 * By using this source code in any fashion, you are agreeing to be bound by the terms of the 
 * MIT License.
 * You must not remove this notice from this software.
 * **********************************************************************************/
#endregion

using System;
using System.Collections.Generic;
using System.Text;
using Irony.Diagnostics;

namespace Irony.CompilerServices {

  public enum CompilerOptions {
    GrammarDebugging = 0x01,
    TraceParser = 0x02,
    AnalyzeCode = 0x10,   //run code analysis; effective only in Module mode
  }
  public enum CompileMode {
    Module,       //default, continuous input file
    VsLineScan,         // line-by-line scanning in VS integration for syntax highlighting
    //ConsoleInput, //line-by-line from console
  }



  // The purpose of this class is to provide a container for information shared 
  // between parser, scanner and token filters.
  // Developers can extend this class to add language-specific properties or methods.
  public class CompilerContext {
    public CompilerOptions Options;
    public CompileMode Mode = CompileMode.Module;
    public readonly Compiler Compiler;
    public readonly Dictionary<string, object> Values = new Dictionary<string, object>();
    public int MaxErrors = 20;
    //State variable used in line scanning mode for VS integration; when Terminal produces incomplete token, it sets 
    // this state to non-zero value; this value identifies this terminal as the one who will continue scanning when
    // it resumes, and the terminal's internal state when there may be several types of multi-line tokens for one terminal.
    // For ex., there maybe several types of string literal like in Python. 
    public VsScannerStateMap ScannerState;
    public ParseTree CurrentParseTree;
    public ParserTrace ParserTrace = new ParserTrace();
    public bool ParserIsRecovering;
    public bool ScannerIsRecovering;

    #region constructors and factory methods
    public CompilerContext(Compiler compiler) {
      this.Compiler = compiler;
#if DEBUG
      Options |= CompilerOptions.GrammarDebugging;
#endif
    }
    #endregion

    #region Helper methods: GetParserState
    public ParserState GetCurrentParserState() {
      try {
        return this.Compiler.Parser.CoreParser.CurrentState;
      } catch {
        return null; 
      }
    }

    #endregion

    #region ParserTracing
    internal ParserTraceEntry AddParserTrace(ParserState state, ParseTreeNode stackTop, ParseTreeNode input) {
      var entry = new ParserTraceEntry(state, stackTop, input);
      ParserTrace.Add(entry);
      return entry;
    }
    #endregion

    #region Events: TokenCreated
    //Note that scanner's output stream may not contain all tokens received by parser. Additional tokens
    // may be generated by intermediate token filters. To listen to token stream at parser input, 
    // use Parser's TokenReceived event. 
    public event EventHandler<TokenEventArgs> TokenCreated;
    TokenEventArgs _tokenArgs = new TokenEventArgs(null);

    internal void OnTokenCreated(Token token) {
      if (TokenCreated == null) return;
      _tokenArgs.Token = token;
      TokenCreated(this, _tokenArgs);
    }
    #endregion

    #region Options helper methods
    public bool OptionIsSet(CompilerOptions option) {
      return (Options & option) != 0;
    }
    public void SetOption(CompilerOptions option, bool value) {
      if (value)
        Options |= option;
      else
        Options &= ~option;
    }
    #endregion

    #region Error handling
    public Token CreateErrorTokenAndReportError(SourceLocation location, string content, string message, params object[] args) {
      if (args != null && args.Length > 0)
        message = string.Format(message, args);
      ReportError(this.Compiler.Parser.CoreParser.CurrentState, location, message);
      var grammar = Compiler.Language.Grammar;
      Token result = new Token(grammar.SyntaxError, location, content, message);
      return result; 
    }
    public void ReportError(SourceLocation location, string message, params object[] args) {
      ReportError(null, location, message, args); 
    }
    public void ReportError(ParserState state, SourceLocation location, string message, params object[] args) {
      if (CurrentParseTree == null) return; 
      if (CurrentParseTree.Errors.Count >= MaxErrors) return;
      if (args != null && args.Length > 0)
        message = string.Format(message, args);
      CurrentParseTree.Errors.Add(new SyntaxError(location, message, state));
    }
    #endregion

  }//class

}
